// internal/service/auth_service.go
package service

import (
	"errors"
	"time"

	"github.com/golang-jwt/jwt/v4"
	"golang.org/x/crypto/bcrypt"

	"github.com/jfastfiler/internal/dao"
	"github.com/jfastfiler/internal/model"
)

type AuthService struct {
	userDAO     *dao.UserDAO
	auditDAO    *dao.AuditDAO
	jwtSecret   string
	tokenExpire time.Duration
	bcryptCost  int
}

func NewAuthService(userDAO *dao.UserDAO, auditDAO *dao.AuditDAO, jwtSecret string, tokenExpire time.Duration, bcryptCost int) *AuthService {
	return &AuthService{
		userDAO:     userDAO,
		auditDAO:    auditDAO,
		jwtSecret:   jwtSecret,
		tokenExpire: tokenExpire,
		bcryptCost:  bcryptCost,
	}
}

type LoginRequest struct {
	Username string `json:"username" binding:"required"`
	Password string `json:"password" binding:"required"`
	Remember bool   `json:"remember"`
}

type LoginResponse struct {
	Token     string    `json:"token"`
	UserInfo  *UserInfo `json:"user_info"`
	ExpiresIn int64     `json:"expires_in"`
}

type UserInfo struct {
	ID         int    `json:"id"`
	Username   string `json:"username"`
	Email      string `json:"email"`
	Role       string `json:"role"`
	RealName   string `json:"real_name"`
	Department string `json:"department"`
	Avatar     string `json:"avatar"`
}

type RegisterRequest struct {
	Username   string `json:"username" binding:"required,min=3,max=50"`
	Password   string `json:"password" binding:"required,min=6,max=100"`
	Email      string `json:"email" binding:"required,email"`
	RealName   string `json:"real_name" binding:"required"`
	Department string `json:"department"`
}

func (s *AuthService) Login(req *LoginRequest, ip, userAgent string) (*LoginResponse, error) {
	user, err := s.userDAO.GetByUsername(req.Username)
	if err != nil {
		s.auditDAO.Create(&model.AuditLog{
			UserID:    0,
			Action:    "login_failed",
			Details:   "用户不存在: " + req.Username,
			IP:        ip,
			UserAgent: userAgent,
			Status:    "failed",
		})
		return nil, errors.New("用户名或密码错误")
	}

	if user.Status == 0 {
		s.auditDAO.Create(&model.AuditLog{
			UserID:    user.ID,
			Action:    "login_failed",
			Details:   "账户已被禁用",
			IP:        ip,
			UserAgent: userAgent,
			Status:    "failed",
		})
		return nil, errors.New("账户已被禁用")
	}

	if user.Status == 2 {
		s.auditDAO.Create(&model.AuditLog{
			UserID:    user.ID,
			Action:    "login_failed",
			Details:   "账户已被锁定",
			IP:        ip,
			UserAgent: userAgent,
			Status:    "failed",
		})
		return nil, errors.New("账户已被锁定")
	}

	err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password))
	if err != nil {
		s.auditDAO.Create(&model.AuditLog{
			UserID:    user.ID,
			Action:    "login_failed",
			Details:   "密码错误",
			IP:        ip,
			UserAgent: userAgent,
			Status:    "failed",
		})
		return nil, errors.New("用户名或密码错误")
	}

	// 更新登录信息
	s.userDAO.UpdateLoginInfo(user.ID, ip)

	// 生成JWT token
	token, expiresIn, err := s.generateToken(user, req.Remember)
	if err != nil {
		return nil, err
	}

	// 记录登录日志
	s.auditDAO.Create(&model.AuditLog{
		UserID:    user.ID,
		Action:    "login",
		IP:        ip,
		UserAgent: userAgent,
		Status:    "success",
	})

	userInfo := &UserInfo{
		ID:         user.ID,
		Username:   user.Username,
		Email:      user.Email,
		Role:       user.Role,
		RealName:   user.RealName,
		Department: user.Department,
		Avatar:     user.Avatar,
	}

	return &LoginResponse{
		Token:     token,
		UserInfo:  userInfo,
		ExpiresIn: expiresIn,
	}, nil
}

func (s *AuthService) Register(req *RegisterRequest, createdBy int) error {
	// 检查用户名是否已存在
	_, err := s.userDAO.GetByUsername(req.Username)
	if err == nil {
		return errors.New("用户名已存在")
	}

	// 检查邮箱是否已存在
	_, err = s.userDAO.GetByEmail(req.Email)
	if err == nil {
		return errors.New("邮箱已存在")
	}

	// 加密密码
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), s.bcryptCost)
	if err != nil {
		return errors.New("密码加密失败")
	}

	user := &model.User{
		Username:     req.Username,
		PasswordHash: string(hashedPassword),
		Email:        req.Email,
		RealName:     req.RealName,
		Department:   req.Department,
		Role:         "user",
		Status:       1,
		CreatedBy:    createdBy,
	}

	return s.userDAO.Create(user)
}

func (s *AuthService) generateToken(user *model.User, remember bool) (string, int64, error) {
	expireDuration := s.tokenExpire
	if remember {
		expireDuration = s.tokenExpire * 24 * 7 // 记住我：7天
	}

	expiresAt := time.Now().Add(expireDuration)
	claims := jwt.MapClaims{
		"user_id":  user.ID,
		"username": user.Username,
		"role":     user.Role,
		"exp":      expiresAt.Unix(),
		"iat":      time.Now().Unix(),
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString([]byte(s.jwtSecret))
	if err != nil {
		return "", 0, err
	}

	return tokenString, int64(expireDuration.Seconds()), nil
}

func (s *AuthService) ValidateToken(tokenString string) (*model.User, error) {
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		return []byte(s.jwtSecret), nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		userID := int(claims["user_id"].(float64))
		return s.userDAO.GetByID(userID)
	}

	return nil, errors.New("无效的token")
}

func (s *AuthService) RefreshToken(oldToken string) (*LoginResponse, error) {
	user, err := s.ValidateToken(oldToken)
	if err != nil {
		return nil, err
	}

	token, expiresIn, err := s.generateToken(user, false)
	if err != nil {
		return nil, err
	}

	userInfo := &UserInfo{
		ID:         user.ID,
		Username:   user.Username,
		Email:      user.Email,
		Role:       user.Role,
		RealName:   user.RealName,
		Department: user.Department,
		Avatar:     user.Avatar,
	}

	return &LoginResponse{
		Token:     token,
		UserInfo:  userInfo,
		ExpiresIn: expiresIn,
	}, nil
}

func (s *AuthService) ChangePassword(userID int, oldPassword, newPassword string) error {
	user, err := s.userDAO.GetByID(userID)
	if err != nil {
		return errors.New("用户不存在")
	}

	err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(oldPassword))
	if err != nil {
		return errors.New("原密码错误")
	}

	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), s.bcryptCost)
	if err != nil {
		return errors.New("密码加密失败")
	}

	user.PasswordHash = string(hashedPassword)
	return s.userDAO.Update(user)
}

func (s *AuthService) ResetPassword(userID int, newPassword string, resetBy int) error {
	user, err := s.userDAO.GetByID(userID)
	if err != nil {
		return errors.New("用户不存在")
	}

	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), s.bcryptCost)
	if err != nil {
		return errors.New("密码加密失败")
	}

	user.PasswordHash = string(hashedPassword)
	return s.userDAO.Update(user)
}